package com.licode.codekit.db;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;

/**
 * 遍历获取数据库所有表信息
 *
 * @author licode
 */
public class AssembledDb {
    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException ignore) {
        }
    }

    public static ClassLoader dbLoader;

    public static boolean dbDriverValid() {
        return null != dbLoader;
    }

    public static void initDbClassloader(String dbDriverPath, boolean forceInit) {
        if (null != dbLoader) {
            return;
        }
        File jarFile = new File(dbDriverPath);
        URL dbUrl;
        try {
            if (jarFile.isFile() && jarFile.exists()) {
                dbUrl = jarFile.toURI().toURL();
                dbLoader = URLClassLoader.newInstance(
                        new URL[] { dbUrl },
                        AssembledDb.class.getClassLoader());
            }
        } catch (MalformedURLException ignore) {
            dbLoader = null;
        }

        if (null != dbLoader) {
            try {
                dbLoader.loadClass("com.mysql.cj.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                dbLoader = null;
            }
        }
    }

    List<Table> tables = new ArrayList<>();
    List<Index> indices = new ArrayList<>();

    public List<Table> getTables() {
        return tables;
    }

    public List<Index> getIndices() {
        return indices;
    }

    public AssembledDb() {

    }

    public boolean initTables(String url, String user, String pwd) {
        boolean succ = false;
        Driver driver = null;
        Properties props = new Properties();
        props.setProperty("user", user);
        props.setProperty("password", pwd);
        props.setProperty("remarks", "true");
        props.setProperty("useInformationSchema", "true");

        getTables().clear();

        try {
            Class<?> clazz;
            if (null != dbLoader) {
                clazz = dbLoader.loadClass("com.mysql.cj.jdbc.Driver");
            } else {
                clazz = AssembledDb.class.getClassLoader().loadClass("com.mysql.cj.jdbc.Driver");
            }
            driver = (Driver) clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        Connection conn = null;

        try {
            if (null == driver) {
                conn = DriverManager.getConnection(url, props);
            } else {
                conn = driver.connect(url, props);
            }
            if (null != conn) {
                DatabaseMetaData metaData = conn.getMetaData();
                ResultSet tableRs = metaData.getTables(conn.getCatalog(),
                        conn.getCatalog(), "%", new String[]{"TABLE"});
                parseTables(conn, metaData, tableRs);
            }
            succ = true;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (null != conn) {
                try {
                    conn.close();
                } catch (SQLException ignore) {
                }
            }
        }

        return succ;
    }

    private void parseTables(Connection conn,
                             DatabaseMetaData metaData,
                             ResultSet tableRs) throws SQLException {
        while (tableRs.next()) {
            String tableName = tableRs.getString("TABLE_NAME");
            List<Column> columns = new ArrayList<>();
            Table table = new Table(tableName, tableRs.getString("REMARKS"), columns);
            String primaryColumn = null;
            ResultSet idxRs = metaData.getIndexInfo(conn.getCatalog(), conn.getCatalog(), tableName, false, false);

            while (idxRs.next()) {
                String indexName = idxRs.getString("INDEX_NAME");
                if ("PRIMARY".equals(indexName)) {
                    primaryColumn = idxRs.getString("COLUMN_NAME");
                } else {
                    // handleIndexs(idxRs, tableName, indexName);
                }
            }

            ResultSet columnRs = metaData.getColumns(conn.getCatalog(), conn.getCatalog(), tableName, "%");
            while (columnRs.next()) {
                Column column = new Column();
                column.setName(columnRs.getString("COLUMN_NAME"));
                column.setType(columnRs.getString("TYPE_NAME").toLowerCase());
                column.setLength(columnRs.getString("COLUMN_SIZE"));
                column.setDecimalDigits(columnRs.getString("DECIMAL_DIGITS"));
                column.setNotNull(!columnRs.getBoolean("NULLABLE"));
                column.setPrimary(column.getName().equals(primaryColumn));
                column.setComment(columnRs.getString("REMARKS"));
                columns.add(column);
                if (column.isPrimary()) {
                    table.setPrimaryCol(column);
                }
            }
            tables.add(table);
        }
    }

    private void handleIndexs(ResultSet idxRs, String tableName, String indexName) throws SQLException {
        List<Index> exist = indices.stream().filter(o -> o.getTableName().equals(tableName) &&
                o.getIndexName().equals(indexName)).collect(Collectors.toList());
        Index index;
        if (exist != null && exist.size() > 0) {
            index = exist.get(0);
            index.setParams(index.getParams() + "," + idxRs.getString("COLUMN_NAME"));
        } else {
            index = new Index();
            index.setTableName(tableName);
            index.setIndexName(indexName);
            index.setUnique(!idxRs.getBoolean("NON_UNIQUE"));
            index.setParams(idxRs.getString("COLUMN_NAME"));
            indices.add(index);
        }
    }
}
